package com.ap.core.util;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * Decorator pattern that allows a standard map type (extends {@link java.util.Map}) to be presented as a TypedMap.
 */
public class TypedMapDecorator implements TypedMap {

    private final Map<TypedKey, Object> decoratedMap;

    /**
     * Construct an instance of TypedMap by decorating a standard map type.
     * @param decoratedMap the standard map to decorate in order to make it behave as a TypedMap.
     */
    public TypedMapDecorator(Map<TypedKey, Object> decoratedMap) {
        this.decoratedMap = decoratedMap;
    }

    @Override
    public int size() {
        return decoratedMap.size();
    }

    @Override
    public boolean isEmpty() {
        return decoratedMap.isEmpty();
    }

    @Override
    public boolean containsKey(TypedKey key) {
        return decoratedMap.containsKey(key);
    }

    @Override
    public boolean containsValue(Object value) {
        return decoratedMap.containsValue(value);
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> Nullable<T> get(TypedKey<T> key) {
        return Nullable.nullable((T)decoratedMap.get(key));    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> Nullable<T> put(TypedKey<T> key, T value) {
        return Nullable.nullable((T) decoratedMap.put(key, value));
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> Nullable<T> remove(TypedKey<T> key) {
        return Nullable.nullable((T) decoratedMap.remove(key));
    }

    @Override
    public void putAll(TypedMap m) {
        decoratedMap.putAll(m.asStandardMap());
    }

    @Override
    public void clear() {
        decoratedMap.clear();
    }

    @Override
    public Set<TypedKey> keySet() {
        return decoratedMap.keySet();
    }

    @Override
    public Collection<Object> values() {
        return decoratedMap.values();
    }

    @Override
    public Set<Map.Entry<TypedKey,Object>> entrySet() {
        return decoratedMap.entrySet();
    }

    @Override
    public Map<TypedKey, Object> asStandardMap() {
        HashMap<TypedKey, Object> typedKeyObjectHashMap = new HashMap<>();
        typedKeyObjectHashMap.putAll(decoratedMap);
        return typedKeyObjectHashMap;
    }

    /**
     * Convenience / functional method for instantiating a default instance wrapping a {@link HashMap}.
     * @return a new instance of a {@link TypedMap}.
     */
    public static TypedMap newTypedMap(){
        return new TypedMapDecorator(new HashMap<TypedKey, Object>());
    }
}
